package com.ld.shieldsb.common.core.reflect;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import com.ld.shieldsb.common.core.util.DateUtil;
import com.ld.shieldsb.common.core.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 不考虑mvc的情况
 * 
 * @ClassName ModelUtil
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2019年6月26日 下午4:54:10
 *
 */
@Slf4j
public class ModelUtil {

    private static Map<String, Object> model2map(Object modelBean, Class<?> modelType, boolean hasNullField) {
        Map<String, Object> map = new HashMap<String, Object>();
        Field[] fields = modelType.getDeclaredFields();
        for (Field field : fields) {
            // 判断是否为private属性
            if (field.getModifiers() == 2) {
                String name = field.getName();
                try {
                    field.setAccessible(true);
                    Object value = field.get(modelBean);
                    if (hasNullField || value != null) {
                        map.put(name, value);
                    }
                } catch (Exception e) {
                    log.error("", e);
                }
            }
        }
        return map;
    }

    /**
     * 只获取当前类的field的map，不包含父级
     * 
     * @Title getThisModelFieldsMap
     * @author 吕凯
     * @date 2019年6月27日 上午8:24:18
     * @param modelType
     * @return Map<String,Field>
     */
    private static Map<String, Field> getThisModelFieldsMap(Class<?> modelType) {
        Map<String, Field> fieldMap = new HashMap<String, Field>();
        Field[] fields = modelType.getDeclaredFields();
        for (Field field : fields) {
            String name = field.getName();
            fieldMap.put(name.toLowerCase(), field);
        }

        return fieldMap;
    }

    // 获取所有父类列表，直到Object类（不含Object类）
    private static List<Class<?>> getSuperTypeList(Class<?> type) {
        List<Class<?>> classList = new LinkedList<Class<?>>();
        classList.add(type);
        Class<?> tempType = type;
        while (!(tempType = tempType.getSuperclass()).equals(Object.class)) {
            classList.add(0, tempType);
        }
        return classList;
    }

    /**
     * 
     * mdoel转成map
     * 
     * @Title model2map
     * @param modelBean
     * @param hasNullField
     *            是否包含空值
     * @return Map<String,Object>
     */
    public static Map<String, Object> model2map(Object modelBean, boolean hasNullField) {
        Map<String, Object> map = new HashMap<String, Object>();
        List<Class<?>> modelTypeList = getSuperTypeList(modelBean.getClass()); // 获取所有父类
        for (Class<?> modelType : modelTypeList) {
            map.putAll(model2map(modelBean, modelType, hasNullField));
        }

        return map;
    }

    public static Map<String, Object> model2map(Object modelBean) {
        return model2map(modelBean, false);
    }

    /**
     * 获取field的Map，包含父类
     * 
     * @Title getFieldMap
     * @author 吕凯
     * @date 2017年11月27日 下午2:08:26
     * @param modelType
     * @param fieldNameFirst
     *            field的名字优先，false为注解名优先
     * @return Map<String,Field>
     */
    public static Map<String, Field> getModelFieldsMap(Class<?> modelType) {
        Map<String, Field> map = new HashMap<String, Field>();
        List<Class<?>> modelTypeList = getSuperTypeList(modelType); // 获取所有父类
        for (Class<?> type : modelTypeList) {
            map.putAll(getThisModelFieldsMap(type));
        }
        return map;
    }

    /**
     * 
     * 复制model
     * 
     * @Title coptyModel
     * @param fromModel
     * @param toModel
     *            void
     */
    public static void coptyModel(Object fromModel, Object toModel) {
        Map<String, Field> fromModelMap = getModelFieldsMap(fromModel.getClass());
        Map<String, Field> toModelMap = getModelFieldsMap(toModel.getClass());
        for (Entry<String, Field> e : toModelMap.entrySet()) {
            Field fromField = fromModelMap.get(e.getKey());
            // 只有复制私有变量
            if (fromField != null && fromField.getModifiers() == 2) {
                try {
                    fromField.setAccessible(true);
                    Object value = fromField.get(fromModel);
                    Field field = e.getValue();
                    if (value != null && value.getClass().equals(field.getType())) {
                        field.setAccessible(true);
                        field.set(toModel, value);

                    }
                } catch (Exception e1) {
                    log.error("", e1);
                }
            }
        }
    }

    /**
     * 
     * map类型转成model
     * 
     * @Title map2model
     * @param map
     *            map数值
     * @param modelType
     *            model类型
     * @return T
     */
    public static <T> T map2model(Map<String, Object> map, Class<T> modelType) {
        try {
            Map<String, Object> newMap = new HashMap<String, Object>();
            for (String key : map.keySet()) {
                Object value = map.get(key);
                if (key != null && value != null) {
                    newMap.put(key.toLowerCase(), value);
                }
            }

            T modelBean = modelType.newInstance();
            Map<String, Field> fieldMap = getModelFieldsMap(modelType);
            for (Field field : fieldMap.values()) {
                // 判断是否为private属性
                if (field.getModifiers() == 2) {
                    String name = field.getName();
                    field.setAccessible(true);
                    Object value = newMap.get(name.toLowerCase());
                    try {
                        if (field.getType().equals(Date.class) && value instanceof String) {
                            // 如果model中字段是Date类型，而map中对应值是String时，需要把String转换为Date
                            String strValue = value == null ? "" : value.toString();
                            if (strValue.indexOf("/") > -1) {
                                value = DateUtil.str2Date(strValue, "yyyy/MM/dd");
                            } else {
                                value = DateUtil.str2Date(strValue);
                            }
                        } else {
                            value = ClassUtil.obj2T(value, field.getType());
                        }

                        field.set(modelBean, value);
                    } catch (Exception e) {
                        log.error("字段设置错误，字段名：“" + name + "”，字段值：“" + value + "”，" + e.getMessage());
                    }
                }
            }
            return modelBean;
        } catch (Exception e) {
            log.error("", e);
        }
        return null;
    }

    /**
     * 
     * 转换所有属性为null且为String类型==》""
     * 
     * @Title confirmValues
     * @param modelBean
     *            实体bean void
     */
    public static void confirmValues(Object modelBean) {
        Map<String, Field> fieldMap = getModelFieldsMap(modelBean.getClass());
        for (Field field : fieldMap.values()) {
            // 判断是否为private属性
            if (field.getModifiers() == 2) {
                try {
                    field.setAccessible(true);
                    Object value = field.get(modelBean);
                    if (value == null) {
                        if (field.getType().equals(String.class)) {
                            field.set(modelBean, "");
                        }
                    }
                } catch (Exception e) {
                    log.error("", e);
                }
            }
        }
    }

    public static Field getField(Class<?> clazz, String name) {
        if (clazz == null || clazz.equals(Object.class)) {
            return null;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            if (fieldName.equals(name)) {
                return field;
            }
        }
        return getField(clazz.getSuperclass(), name);
    }

    /**
     * 
     * 获取model的某个属性值,之前嵌套属性，user.role.roleName、user.userName
     * 
     * @Title getModelValue
     * @param model
     *            实体model
     * @param fieldName
     *            字段值
     * @return Object
     */
    public static Object getModelValue(Object model, String fieldName) {
        // map
        if (model instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) model;
            return map.get(fieldName);
        }
        try {
            String childFieldName = null;
            if (fieldName.contains(".")) {
                childFieldName = StringUtils.substringAfter(fieldName, ".");
                fieldName = StringUtils.substringBefore(fieldName, ".");
            }
            Field field = getField(model.getClass(), fieldName);
            if (field != null) {
                field.setAccessible(true);
                Object obj = field.get(model);
                return childFieldName == null ? obj : getModelValue(obj, childFieldName);
            }
            log.info("getModelValue属性未找到：" + fieldName + " class:" + model.getClass());
        } catch (Exception e) {
            log.error(model + "中找不到" + fieldName + "属性", e);
            log.warn(e.getMessage());
        }
        return null;
    }

    /**
     * model对象中是否存在属性fieldName，不支持嵌套
     * 
     * @Title hasField
     * @author 吕凯
     * @date 2018年6月26日 下午4:59:44
     * @param model
     * @param fieldName
     * @return boolean
     */
    public static <T> boolean hasField(T model, String fieldName) {
        try {
            Field field = getField(model.getClass(), fieldName);
            return field != null;
        } catch (Exception e) {
            log.warn(e.getMessage());
        }
        return false;
    }

    public static boolean hasField(Class<?> clazz, String fieldName) {
        try {
            Field field = getField(clazz, fieldName);
            return field != null;
        } catch (Exception e) {
            log.warn(e.getMessage());
        }
        return false;
    }

    /**
     * 
     * 给model的某个属性赋值，不支持嵌套
     * 
     * @Title setModelValue
     * @param model
     *            实体model
     * @param fieldName
     *            字段
     * @param value
     *            字段值
     * @return boolean
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static boolean setModelValue(Object model, String fieldName, Object value) {
        // map
        if (model instanceof Map) {
            Map map = (Map) model;
            map.put(fieldName, value);
            return true;
        }
        try {
            Field field = getField(model.getClass(), fieldName);
            if (field != null) {
                field.setAccessible(true);
                if (value != null && !field.getType().equals(value.getClass())) {
                    value = ClassUtil.obj2T(value, field.getType());
                }
                field.set(model, value);
                return true;
            }
            log.error("setModelValue属性未找到", new Exception());
        } catch (Exception e) {
            log.error("设置" + model + "中属性" + fieldName + "出错", e);
        }
        return false;
    }

    public static <T> Object getModelId(T modelBean) {
        return getModelValue(modelBean, "id");
    }

    /**
     * 获取值，如果值为null则返回默认值
     * 
     * @Title getValueOrDefault
     * @author 吕凯
     * @date 2019年7月12日 下午3:16:07
     * @param obj
     * @param defaultV
     * @return Object
     */
    public static Object getValueOrDefault(Object obj, Object defaultV) {
        return Optional.ofNullable(obj).orElse(defaultV);
    }

}
